package com.wms.logger;

import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;

import com.orhanobut.logger.AndroidLogAdapter;
import com.orhanobut.logger.DiskLogAdapter;
import com.orhanobut.logger.FormatStrategy;
import com.orhanobut.logger.PrettyFormatStrategy;

import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;

/**
 * Created by 王梦思 on 2015/12/16.
 */
public class Logger {

    private static Logger mInstance;

    private static Logger getInstance(Builder builder) {
        if (mInstance == null) {
            try {
                mInstance = new Logger(builder);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return mInstance;
    }

    private Logger(final Builder builder) {

        if (builder == null || !builder.isDebug) return;

        com.orhanobut.logger.Logger.clearLogAdapters();

        File logPath = new File(getLogPath(builder.context, builder.logPath));
        if (builder.saveLogCount > 0) {
            if (!TextUtils.isEmpty(logPath.getAbsolutePath()) && logPath.exists()) {
                String[] logs = logPath.list();
                if (logs != null && logs.length > 0) {
                    try {
                        long[] logTimes = new long[logs.length];
                        for (int i = 0; i < logs.length; i++) {
                            logTimes[i] = builder.logFileFormat.parse(logs[i]).getTime();
                        }
                        Arrays.sort(logTimes);
                        if (logTimes.length > builder.saveLogCount) {
                            for (int i = 0; i < logTimes.length - builder.saveLogCount; i++) {
                                deleteDir(new File(logPath + File.separator + builder.logFileFormat.format(logTimes[i])));
                            }
                        }
                    } catch (ParseException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        if (builder.deleteOnLaunch && builder.saveFile) {
            deleteDir(logPath);
        }

        FormatStrategy formatStrategy = PrettyFormatStrategy.newBuilder()
                .showThreadInfo(builder.showThreadInfo)  // (Optional) Whether to show thread info or not. Default true
                .methodCount(builder.methodCount)         // (Optional) How many method line to show. Default 2
                .methodOffset(builder.methodOffset)        // (Optional) Hides internal method calls up to offset. Default 5
                .tag(builder.tag)   // (Optional) Global tag for every log. Default PRETTY_LOGGER
                .build();
        com.orhanobut.logger.Logger.addLogAdapter(new AndroidLogAdapter(formatStrategy) {
            @Override
            public boolean isLoggable(int priority, String tag) {
                return builder.isDebug;
            }
        });

        if (builder.saveFile) {
            MyFormatStrategy strategy = MyFormatStrategy
                    .newBuilder()
                    .tag(builder.tag)
                    .logPath(builder.logPath)
                    .context(builder.context)
                    .logFileFormat(builder.logFileFormat)
                    .showThreadInfo(builder.showThreadInfo)
                    .maxFileSize(builder.maxFileSize)
                    .build();
            com.orhanobut.logger.Logger.addLogAdapter(new DiskLogAdapter(strategy) {
                @Override
                public boolean isLoggable(int priority, @Nullable String tag) {
                    return builder.isDebug;
                }
            });
        }
    }

    private static boolean deleteDir(File dir) {
        if (dir.isDirectory()) {
            String[] children = dir.list();
            for (String aChildren : children) {
                boolean success = deleteDir(new File(dir, aChildren));
                if (!success) {
                    return false;
                }
            }
        }
        // 目录此时为空，可以删除
        return dir.delete();
    }

    @NonNull
    public static Builder newBuilder() {
        return new Builder();
    }

    public static class Builder {
        String tag = "WMS_LOGGER";
        boolean isDebug = false;
        int methodCount = 0;
        int methodOffset = 5;
        boolean showThreadInfo = false;
        boolean saveFile;
        Context context;
        String logPath;
        /**
         * 保存7个日志文件
         */
        int saveLogCount = 7;
        /**
         * 启动应用删除日志
         */
        boolean deleteOnLaunch;
        @SuppressLint("SimpleDateFormat")
        SimpleDateFormat logFileFormat = new SimpleDateFormat("yyyy-MM-dd");
        int maxFileSize = 1024 * 1024;

        private Builder() {
        }

        @NonNull
        public Builder maxFileSize(int val) {
            maxFileSize = val;
            return this;
        }

        public Builder tag(String tag) {
            this.tag = tag;
            return this;
        }

        public Builder isDebug(boolean debug) {
            isDebug = debug;
            return this;
        }

        public Builder methodCount(int cout) {
            methodCount = cout;
            return this;
        }

        public Builder methodOffset(int cout) {
            methodOffset = cout;
            return this;
        }

        public Builder showThreadInfo(boolean showThreadInfo) {
            this.showThreadInfo = showThreadInfo;
            return this;
        }

        public Builder saveFile(boolean save) {
            this.saveFile = save;
            return this;
        }

        public Builder context(Context context) {
            this.context = context;
            return this;
        }

        public Builder logPath(String logPath) {
            this.logPath = logPath;
            return this;
        }

        public Builder deleteOnLaunch(boolean deleteOnLaunch) {
            this.deleteOnLaunch = deleteOnLaunch;
            return this;
        }

        public void build() {
            getInstance(this);
        }

        public void build(boolean reBuild) {
            mInstance = null;
            getInstance(this);
        }

        public Builder saveLogCount(int count) {
            saveLogCount = count;
            return this;
        }

        public Builder logFileFormat(SimpleDateFormat format) {
            this.logFileFormat = format;
            return this;
        }
    }


    //info-----begin
    public static void i(Object msg) {
        log(Type.INFO, msg);
    }

    public static void i(Object msg1, Object msg2) {
        log(Type.INFO, msg1, msg2);
    }

    public static void i(Object msg1, Object msg2, Object msg3) {
        log(Type.INFO, msg1, msg2, msg3);
    }

    public static void i(Object msg1, Object msg2, Object msg3, Object msg4) {
        log(Type.INFO, msg1, msg2, msg3, msg4);
    }

    public static void i(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5) {
        log(Type.INFO, msg1, msg2, msg3, msg4, msg5);
    }

    public static void i(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6) {
        log(Type.INFO, msg1, msg2, msg3, msg4, msg5, msg6);
    }

    public static void i(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6, Object msg7) {
        log(Type.INFO, msg1, msg2, msg3, msg4, msg5, msg6, msg7);
    }

    public static void i(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6, Object msg7, Object msg8) {
        log(Type.INFO, msg1, msg2, msg3, msg4, msg5, msg6, msg7, msg8);
    }
    //info-----end

    //error-----begin
    public static void e(Object msg) {
        log(Type.ERROR, msg);
    }

    public static void e(Object msg1, Object msg2) {
        log(Type.ERROR, msg1, msg2);
    }

    public static void e(Object msg1, Object msg2, Object msg3) {
        log(Type.ERROR, msg1, msg2, msg3);
    }

    public static void e(Object msg1, Object msg2, Object msg3, Object msg4) {
        log(Type.ERROR, msg1, msg2, msg3, msg4);
    }

    public static void e(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5) {
        log(Type.ERROR, msg1, msg2, msg3, msg4, msg5);
    }

    public static void e(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6) {
        log(Type.ERROR, msg1, msg2, msg3, msg4, msg5, msg6);
    }

    public static void e(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6, Object msg7) {
        log(Type.ERROR, msg1, msg2, msg3, msg4, msg5, msg6, msg7);
    }

    public static void e(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6, Object msg7, Object msg8) {
        log(Type.ERROR, msg1, msg2, msg3, msg4, msg5, msg6, msg7, msg8);
    }
    //error-----end

    //VERBOSE-----begin
    public static void v(Object msg) {
        log(Type.VERBOSE, msg);
    }

    public static void v(Object msg1, Object msg2) {
        log(Type.VERBOSE, msg1, msg2);
    }

    public static void v(Object msg1, Object msg2, Object msg3) {
        log(Type.VERBOSE, msg1, msg2, msg3);
    }

    public static void v(Object msg1, Object msg2, Object msg3, Object msg4) {
        log(Type.VERBOSE, msg1, msg2, msg3, msg4);
    }

    public static void v(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5) {
        log(Type.VERBOSE, msg1, msg2, msg3, msg4, msg5);
    }

    public static void v(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6) {
        log(Type.VERBOSE, msg1, msg2, msg3, msg4, msg5, msg6);
    }

    public static void v(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6, Object msg7) {
        log(Type.VERBOSE, msg1, msg2, msg3, msg4, msg5, msg6, msg7);
    }

    public static void v(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6, Object msg7, Object msg8) {
        log(Type.VERBOSE, msg1, msg2, msg3, msg4, msg5, msg6, msg7, msg8);
    }
    //VERBOSE-----end

    //DEBUG-----begin
    public static void d(Object msg) {
        log(Type.DEBUG, msg);
    }

    public static void d(Object msg1, Object msg2) {
        log(Type.DEBUG, msg1, msg2);
    }

    public static void d(Object msg1, Object msg2, Object msg3) {
        log(Type.DEBUG, msg1, msg2, msg3);
    }

    public static void d(Object msg1, Object msg2, Object msg3, Object msg4) {
        log(Type.DEBUG, msg1, msg2, msg3, msg4);
    }

    public static void d(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5) {
        log(Type.DEBUG, msg1, msg2, msg3, msg4, msg5);
    }

    public static void d(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6) {
        log(Type.DEBUG, msg1, msg2, msg3, msg4, msg5, msg6);
    }

    public static void d(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6, Object msg7) {
        log(Type.DEBUG, msg1, msg2, msg3, msg4, msg5, msg6, msg7);
    }

    public static void d(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6, Object msg7, Object msg8) {
        log(Type.DEBUG, msg1, msg2, msg3, msg4, msg5, msg6, msg7, msg8);
    }
    //DEBUG-----end

    //warm-----begin
    public static void w(Object msg) {
        log(Type.WARM, msg);
    }

    public static void w(Object msg1, Object msg2) {
        log(Type.WARM, msg1, msg2);
    }

    public static void w(Object msg1, Object msg2, Object msg3) {
        log(Type.WARM, msg1, msg2, msg3);
    }

    public static void w(Object msg1, Object msg2, Object msg3, Object msg4) {
        log(Type.WARM, msg1, msg2, msg3, msg4);
    }

    public static void w(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5) {
        log(Type.WARM, msg1, msg2, msg3, msg4, msg5);
    }

    public static void w(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6) {
        log(Type.WARM, msg1, msg2, msg3, msg4, msg5, msg6);
    }

    public static void w(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6, Object msg7) {
        log(Type.WARM, msg1, msg2, msg3, msg4, msg5, msg6, msg7);
    }

    public static void w(Object msg1, Object msg2, Object msg3, Object msg4, Object msg5, Object msg6, Object msg7, Object msg8) {
        log(Type.WARM, msg1, msg2, msg3, msg4, msg5, msg6, msg7, msg8);
    }
    //warm-----end

    private static void log(Type type, Object... msgs) {
        StringBuilder log = new StringBuilder();
        for (Object msg : msgs) {
            if (msg != null) {
                log.append(msg.toString());
            }
        }
        if (type == Type.WARM) {
            com.orhanobut.logger.Logger.w(log.toString());
        } else if (type == Type.INFO) {
            com.orhanobut.logger.Logger.i(log.toString());
        } else if (type == Type.VERBOSE) {
            com.orhanobut.logger.Logger.v(log.toString());
        } else if (type == Type.DEBUG) {
            com.orhanobut.logger.Logger.d(log.toString());
        } else if (type == Type.ERROR) {
            com.orhanobut.logger.Logger.e(log.toString());
        }
    }

    private enum Type {
        INFO, WARM, VERBOSE, DEBUG, ERROR
    }

    public static void e(Throwable e) {
        com.orhanobut.logger.Logger.w(getThrowableString(e));
    }

    private static String getThrowableString(Throwable e) {
        Writer writer = new StringWriter();
        PrintWriter printWriter = new PrintWriter(writer);
        while (e != null) {
            e.printStackTrace(printWriter);
            e = e.getCause();
        }
        String text = writer.toString();
        printWriter.close();
        return text;
    }

    public static void json(String json) {
        com.orhanobut.logger.Logger.json(json);
    }

    public static void xml(String xml) {
        com.orhanobut.logger.Logger.xml(xml);
    }

    static String getLogPath(Context context, String path) {
        String diskPath = path;
        if (TextUtils.isEmpty(path)) {
            if (context != null) {
                File file = context.getExternalFilesDir(null);
                if (file != null) {
                    diskPath = file.getAbsolutePath();
                } else {
                    diskPath = Environment.getExternalStorageDirectory().getAbsolutePath();
                }
            } else {
                diskPath = Environment.getExternalStorageDirectory().getAbsolutePath();
            }
        }
        return diskPath + File.separatorChar + "logger";
    }
}
